Skip to content

Volume Renderer v2#1120

Open
ElpadoCan wants to merge 37 commits into
mainfrom
renderer_3d_v2
Open

Volume Renderer v2#1120
ElpadoCan wants to merge 37 commits into
mainfrom
renderer_3d_v2

Conversation

@ElpadoCan

@ElpadoCan ElpadoCan commented Jun 15, 2026

Copy link
Copy Markdown
Collaborator

Taking inspiration from PRs #1110, #1102, #1096, #1106, and from https://github.com/keejkrej/acdc-son, this PR implements a 3D volume renderer that can be launched from a Python script (see below), or from the module 3 GUI.

Minimal code:

renderer = VolumeRendererWindow()
renderer.set_volume(zstack_array)   # (Z, Y, X) numpy array
renderer.run()

Features:

  • Lut colormap and slider for each channel
  • Auto and Reset buttons above each lut slider
  • Gamma correction with slider
  • Opacity slider for each channel
  • Top toolbar with "Home" view and "Save" button
  • "Save" buttons saves a PNG screenshot
  • Multi-channel overlay
  • Overaly segmentation mask
  • List of selectable Cell IDs to show only those cells
  • Clickable segmentation masks with "Ctrl" click to select more than one
  • z-anisotropy correction factor as a numeric control
  • z-anisotropy correction factor with the function scipy.ndimage.zoom

@ElpadoCan ElpadoCan marked this pull request as ready for review July 7, 2026 15:13
@Teranis Teranis requested a review from Copilot July 7, 2026 15:31

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a new VisPy-based 3D volume rendering window (VolumeRendererWindow) with multi-channel LUT/opacity controls, segmentation-mask overlay, and optional points layers, and integrates it into the main Cell-ACDC GUI via a “Launch 3D viewer” action/button.

Changes:

  • Added a new 3D volume-renderer module (canvas + toolbar widgets) with blending utilities and voxel scaling helpers.
  • Extended colormap/gradient and points-layer UI infrastructure (PyQtGraph↔VisPy symbol mapping, gradient shuffle actions, points appearance dialog support).
  • Wired the renderer into the GUI and added demo scripts + data downloader for quick testing.

Reviewed changes

Copilot reviewed 21 out of 22 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
cellacdc/widgets.py Adds VisPy marker symbol combobox, gradient widget refactor, LUT state helpers, and context-menu plumbing used by the 3D renderer.
cellacdc/volume_renderer/utils.py Adds voxel-size→display scaling helper for (Z,Y,X) data.
cellacdc/volume_renderer/gl_blend.py Adds napari-compatible-ish GL blending state helper for multi-volume compositing.
cellacdc/volume_renderer/canvas.py Implements the main VolumeRendererWindow (VisPy canvas, LUTs, labels overlay, points layers, downsampling, camera/home view).
cellacdc/volume_renderer/_widgets.py Adds renderer toolbars (home/save/update + single-channel mode).
cellacdc/volume_renderer/_demos/data/.gitkeep Keeps demo data folder in repo while ignoring downloaded content.
cellacdc/volume_renderer/_demos/_demo_single_channel.py Demo: render a single channel from an existing dataset path.
cellacdc/volume_renderer/_demos/_demo_single_channel_lab_3d.py Demo: render volume + 3D labels with voxel size.
cellacdc/volume_renderer/_demos/_demo_single_channel_lab_3d_with_points.py Demo: render volume + labels + points loaded from CSV.
cellacdc/volume_renderer/_demos/_demo_single_channel_lab_2d.py Demo: render 2D labels replicated along Z.
cellacdc/volume_renderer/_demos/_demo_single_channel_custom_cmap.py Demo: render single channel with a custom 2-color cmap.
cellacdc/volume_renderer/_demos/_demo_multi_channel.py Demo: render multi-channel overlay.
cellacdc/volume_renderer/_demos/_demo_multi_channel_lab_3d_with_points.py Demo: multi-channel + labels + points.
cellacdc/volume_renderer/_demos/_demo_lab_2d.py Demo: labels-only rendering.
cellacdc/volume_renderer/__init__.py Exposes module-local demo data_path.
cellacdc/plot.py Adds VisPy marker symbol typing + PyQtGraph↔VisPy symbol mapping/path lookup.
cellacdc/myutils.py Adds helper to download and extract demo dataset.
cellacdc/gui.py Integrates “Launch 3D viewer” action/button and syncs labels gradient shuffle signals.
cellacdc/colors.py Adds typed colormap helpers and PyQtGraph→VisPy colormap conversion + auto-contrast percentile helper.
cellacdc/apps.py Extends points-layer appearance dialog to support VisPy backend and optional opacity slider.
cellacdc/__init__.py Attempts to import VolumeRendererWindow when GUI is installed (best-effort).
.gitignore Ignores downloaded demo data under cellacdc/volume_renderer/_demos/data/* while keeping .gitkeep.
Comments suppressed due to low confidence (1)

cellacdc/widgets.py:3449

  • ToolButtonCustomColor.paintEvent always calls p.end() in finally, but p is only defined inside the try. If QPainter(self) raises, finally will raise UnboundLocalError, masking the original exception.
    def paintEvent(self, event):
        QToolButton.paintEvent(self, event)
        try:
            p = QPainter(self)
            w, h = self.width(), self.height()
            sf = 0.6
            p.scale(w*sf, h*sf)
            p.translate(0.5/sf, 0.5/sf)
            symbol = plot.PyQtGraphScatterPlotSymbolPathMatter[self.symbol]
            pen = pg.mkPen(color=self.penColor, width=2)
            brush = pg.mkBrush(color=self.brushColor)
            p.setRenderHint(QPainter.RenderHint.Antialiasing)
            p.setPen(pen)
            p.setBrush(brush)
            p.drawPath(symbol)
        except Exception as e:
            traceback.print_exc()
        finally:
            p.end()


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +1076 to +1089
cmaps = None
lut_items_states = None
if lut_item_state is not None:
lut_items_states = {channel_name: cmap}
elif cmap is not None:
cmaps = {channel_name: cmap}

volumes = {channel_name: volume}

self.set_volumes(
volumes,
cmaps=cmaps,
lut_items_states=lut_items_states
)
Comment thread cellacdc/volume_renderer/canvas.py
Comment on lines +130 to +134
self._voxel_size_transform = None
self._lab_ncolors = 256
self._voxel_size_strides_transform = None
self._downsample_strides = None
self._object_labels_list_buttongroup = None
Comment on lines +644 to +654
if self._downsample_strides is None:
strides = tuple(
max(1, int(np.ceil(s / max_size))) for s in vol.shape
)
self._downsample_strides = strides
else:
strides = self._downsample_strides

if all(s == 1 for s in strides):
return vol
return np.ascontiguousarray(vol[::strides[0], ::strides[1], ::strides[2]])

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While we don't guard for it, we don't allow volumes with different shapes. We will add a guard for that

Comment on lines +1213 to +1220
if points_df is not None:
points_xyz = points_df[zyx_columns_names[::-1]].to_numpy()
elif points:
points_xyz = points[:, [2, 1, 0]]
else:
raise ValueError(
"Either 'points' or 'points_df' must be provided."
)
Comment on lines +1356 to +1360
if not self._channels_data:
Z, Y, X = self._lab.shape
dummy_vol = np.zeros((Z, Y, X), dtype=np.float32)
self.set_volume(dummy_vol, channel_name='1')

Comment thread cellacdc/volume_renderer/canvas.py
Comment on lines +914 to +919
for c, (channel, channel_data) in enumerate(self._channels_data.items()):
blending = "translucent_no_depth" if c == 0 else "additive"
node = channel_data.node
node.order = c
node.opacity = channel_data.opacity_slider.value()
node.set_gl_state(**volume_gl_state(blending, first_visible=c==0))
Comment thread cellacdc/gui.py
Comment on lines +3162 to +3165
if not checked:
self._volume_renderer.close()
self._volume_renderer = None
return
Comment thread cellacdc/gui.py Outdated
Comment on lines +3224 to +3225
def onClose3dViewer(self):
self.launch3dViewerAction.setChecked(False)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants